package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.Goods;
import com.atguigu.gmall.model.list.SearchResponseAttrVo;
import com.atguigu.gmall.model.list.SearchResponseTmVo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 商品搜索接口实现类
 */
@Service
public class SearchServiceImpl implements SearchService {


    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 商品搜索
     *
     * @param searchData
     * @return
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchData) {
        try {
            //构建查询条件 参数检验
            SearchRequest searchRequest = buildSearchParams(searchData);
            //执行搜索
            SearchResponse response =
                    restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //解析 并返回结果
            return getSearchResponse(response);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 构建查询条件
     *
     * @param searchData
     * @return
     */

    private SearchRequest buildSearchParams(Map<String, String> searchData) {
        //初始化请求对象
        SearchRequest searchRequest = new SearchRequest("goods_java0107");
        //初始化条件构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //初始化组合查询构造器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //搜索框直接输入查询条件
        //获取查询参数
        String keywords = searchData.get("keywords");
        //检验关键字是否为空，不为空则作为查询条件
        if (!StringUtils.isEmpty(keywords)) {
            //拼接查询条件
            // searchSourceBuilder.query(QueryBuilders.matchQuery("title",keywords));
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keywords));
        }
        //根据分类查询
        //获取分类查询参数
        String category = searchData.get("category");
        //检验参数
        if (!StringUtils.isEmpty(category)) {
            String[] split = category.split(":");
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id", split[0]));
        }
        //根据品牌查询
        //获取参数
        String tradeMark = searchData.get("tradeMark");
        //参数校验
        if (!StringUtils.isEmpty(tradeMark)) {
            String[] split = tradeMark.split(":");
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId", split[0]));
        } else {
            //设置品牌聚合查询的条件
            // terms 表示聚合查询结果的别名 ，field 表示要聚合的域
            searchSourceBuilder.aggregation(
                    AggregationBuilders.terms("aggTmId").field("tmId") //根据品牌Id聚合
                            .subAggregation(AggregationBuilders.terms("aggTmLogoUrl").field("tmLogoUrl"))//根据品牌logo进行聚合
                            .subAggregation(AggregationBuilders.terms("aggTmName").field("tmName")));//根据品牌名字进行聚合
        }

        //根据平台属性查询
        //构建平台属性查询条件
        searchData.entrySet().stream().forEach(params -> {
            String key = params.getKey();
            if (key.startsWith("attr_")) {
                String value = params.getValue();
                String[] split = value.split(":");
                //因为平台属性的值可能有多个，所以用nested来存储
                //构建nested查询要使用的bool查询条件构造器
                BoolQueryBuilder nestedQueryBuilder = QueryBuilders.boolQuery();
                //判断参数里的平台属性Id是否和用户·选的一致
                nestedQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                //获得用户输入的平台属性得值
                nestedQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                //构建nested类型的查询条件
                // QueryBuilders.nestedQuery("attrs",nestedQueryBuilder, ScoreMode.None);
                boolQueryBuilder.must(QueryBuilders.nestedQuery("attrs", nestedQueryBuilder, ScoreMode.None));
            }
        });
        //根据价格查询
        String price = searchData.get("price");
        if (price != null && !StringUtils.isEmpty(price)) {
            //原数据0-500元 或3000元以上
            price = price.replace("元", "").replace("以上", "");
            String[] split = price.split("-");
            //大于等于第一个值
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(split[0]));
            //小于第二个值
            //先判断是否有第二个值
            if (split.length > 1) {
                //有第二个值才进行判断
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lt(split[1]));
            }
        }
        //设置排序条件
        //获取排序的域
        String sortField = searchData.get("sortField");
        //获取排序规则
        String sortRule = searchData.get("sortRule");
        if (sortField != null && !StringUtils.isEmpty(sortField)) {
            //排序条件不为空
            searchSourceBuilder.sort(sortField, SortOrder.valueOf(sortRule));
        } else {
            //没有指定排序规则 默认根据Id 倒序
            searchSourceBuilder.sort("id", SortOrder.DESC);
        }
        //设置分页
        //初始化每页显示条数
        Integer size = 50;
        searchSourceBuilder.size(size);
        //获取页码
        String pageNum = searchData.get("pageNum");
        //页码校验
        int page = getPage(pageNum);
        //
        if (pageNum != null && !StringUtils.isEmpty(pageNum)) {
            // 显示规则为  0-49  50-99
            //es分页时不显示初始数据 因此要显示初始数据那一页要-1
            //输入的是第几页 因此要* 每页显示数
            searchSourceBuilder.from((page - 1) * size);
        }
        //设置组合查询的全部条件
        searchSourceBuilder.query(boolQueryBuilder);
        //设置高亮属性
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<em style=color:red>");
        highlightBuilder.postTags("</em>");
        searchSourceBuilder.highlighter(highlightBuilder);
        //设置平台属性聚合查询的条件
        searchSourceBuilder.aggregation(AggregationBuilders.nested("aggAttrs", "attrs")
                .subAggregation(AggregationBuilders.terms("aggAttrId").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("aggAttrName").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("aggAttrValue").field("attrs.attrValue"))));
        //设置查询条件
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    //页码校验
    private int getPage(String pageNum) {
        try {
            //转换
            int i = Integer.parseInt(pageNum);
            //校验 页码不能小于1 es默认最多展示10000条记录 因为默认每页显示50条记录 因此最多只能显示200页
            if (i < 1 || i > 200) {
                return 1;
            }
            return i;
        } catch (Exception e) {
            //如果输入的不是页码则显示第一页
            return 1;
        }
    }


    /**
     * P
     * 解析并返回查询结果
     *
     * @param response
     * @return
     */
    private Map<String, Object> getSearchResponse(SearchResponse response) {

        //返回结果初始化
        Map<String, Object> result = new HashMap<>();
        //返回商品结果初始化
        List<Goods> goodsList = new ArrayList<>();
        //获取所有命中记录
        SearchHits hits = response.getHits();
        //获取总记录数
        long totalHits = hits.getTotalHits();
        //返回查询到的总记录数
        result.put("totalHits", totalHits);
        //获取迭代器
        Iterator<SearchHit> iterator = hits.iterator();
        //根据迭代器解析结果
        while (iterator.hasNext()) {
            SearchHit next = iterator.next();
            //结果序列化为JSON字符串
            String sourceAsString = next.getSourceAsString();
            //JSON反序列化为对象
            Goods goods = JSONObject.parseObject(sourceAsString, Goods.class);
            //高亮显示
            //取出高亮数据
            Map<String, HighlightField> highlightFields = next.getHighlightFields();
            if (highlightFields != null && !highlightFields.isEmpty()){
                HighlightField highlightField = highlightFields.get("title");
                if (highlightField !=null){
                    Text[] fragments = highlightField.getFragments();
                    if (fragments !=null && fragments.length >0){
                        String title = "";
                        for (Text fragment : fragments) {
                            title +=fragment;
                        }
                        goods.setTitle(title);
                    }
                }
            }
            //将解析对象存储
            goodsList.add(goods);
        }
        //聚合查询
        //解析全部的聚合结果
        Aggregations aggregations = response.getAggregations();
        //解析品牌聚合查询的结果
        List<SearchResponseTmVo> responseTmVoList = getTmAggResult(aggregations);
        result.put("responseTmVoList", responseTmVoList);
        //解析平台属性聚合查询的结果
        Stream<SearchResponseAttrVo> searchAttrAggResult = getSearchAttrAggResult(aggregations);
        result.put("searchAttrAggResult", searchAttrAggResult);
        //返回商品数据
        result.put("goodsList", goodsList);

        //返回结果
        return result;
    }

    /**
     * 解析平台属性聚合的结果
     *
     * @param aggregations
     * @return
     */
    private Stream<SearchResponseAttrVo> getSearchAttrAggResult(Aggregations aggregations) {
        //根据aggAttrs获得解析后的Nested类型的结果
        ParsedNested aggAttrs = aggregations.get("aggAttrs");
        if (aggAttrs == null) {
            return null;
        }
        //获取Nested子聚合平台ID的结果:
        Aggregations subAggregations = aggAttrs.getAggregations();
        ParsedLongTerms aggAttrIds = subAggregations.get("aggAttrId");
        //获取平台属性Id的所有聚合结果的集合
        return aggAttrIds.getBuckets().stream().map(aggAttrId -> {
            //初始化返回参数
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            //获取平台属性的Id
            long attrId = aggAttrId.getKeyAsNumber().longValue();
            searchResponseAttrVo.setAttrId(attrId);
            //获取每个平台属性Id的聚合结果
            //获却aggAttrName 的聚合结果
            ParsedStringTerms aggAttrName = aggAttrId.getAggregations().get("aggAttrName");
            List<? extends Terms.Bucket> nameBuckets = aggAttrName.getBuckets();
            if (nameBuckets != null && !nameBuckets.isEmpty()) {
                String AttrName = nameBuckets.get(0).getKeyAsString();
                searchResponseAttrVo.setAttrName(AttrName);
            }
            //获取aggAttrValue的聚合结果
            ParsedStringTerms aggAttrValue = aggAttrId.getAggregations().get("aggAttrValue");
            List<? extends Terms.Bucket> valueBuckets = aggAttrValue.getBuckets();
            if (valueBuckets != null && !valueBuckets.isEmpty()) {
                List<String> attrValueList = valueBuckets.stream().map(valueBucket -> {
                    return valueBucket.getKeyAsString();
                }).collect(Collectors.toList());
                searchResponseAttrVo.setAttrValueList(attrValueList);
            }
            return searchResponseAttrVo;
        });
    }

    /**
     * 解析品牌聚合结果
     *
     * @param aggregations
     * @return
     */
    private List<SearchResponseTmVo> getTmAggResult(Aggregations aggregations) {
        //获得品牌的聚合结果
        ParsedLongTerms aggTmIdResult = aggregations.get("aggTmId");
        //解析结果非空判断
        if (StringUtils.isEmpty(aggTmIdResult) || aggTmIdResult == null) {
            return null;
        }
        //获取每个品牌Id对应的解析结果
        return aggTmIdResult.getBuckets().stream().map(aggTmId -> {
            //初始化返回参数
            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            //获取品牌的Id
            long tmId = aggTmId.getKeyAsNumber().longValue();
            //将id返回
            searchResponseTmVo.setTmId(tmId);
            //获取每个ID对应的名字的聚合结果
            ParsedStringTerms aggTmName = aggTmId.getAggregations().get("aggTmName");
            List<? extends Terms.Bucket> aggTmNameS = aggTmName.getBuckets();
            if (aggTmNameS != null && !aggTmNameS.isEmpty()) {
                //获取任意一个品牌的名字
                String tmName = aggTmNameS.get(0).getKeyAsString();
                //将名字返回
                searchResponseTmVo.setTmName(tmName);
            }
            //获取每个品牌Id对应的Logo的聚合结果
            ParsedStringTerms aggTmLogoUrl = aggTmId.getAggregations().get("aggTmLogoUrl");
            List<? extends Terms.Bucket> tmLogoUrlBuckets = aggTmLogoUrl.getBuckets();
            if (tmLogoUrlBuckets != null && !tmLogoUrlBuckets.isEmpty()) {
                //获得任意一张LogoUrl
                String tmLogoUrl = tmLogoUrlBuckets.get(0).getKeyAsString();
                //将logoUrl返回
                searchResponseTmVo.setTmLogoUrl(tmLogoUrl);
            }
            return searchResponseTmVo;
        }).collect(Collectors.toList());

    }
}
